package com.kinghunt.misp.service.impl.configx;

import com.alibaba.fastjson.JSON;
import com.kinghunt.misp.common.UploadFileConfig;
import com.kinghunt.misp.common.annotation.Annotation;
import com.kinghunt.misp.common.constant.RedisKeyPrefix;
import com.kinghunt.misp.common.dto.configx.OrgConfigDTO;
import com.kinghunt.misp.common.dto.configx.SysConfigDTO;
import com.kinghunt.misp.common.dto.userx.UserDTO;
import com.kinghunt.misp.common.enums.userx.RoleEnum;
import com.kinghunt.misp.dal.dao.configx.ConfigMetadataDAO;
import com.kinghunt.misp.dal.dao.configx.ConfigValueDAO;
import com.kinghunt.misp.dal.model.configx.ConfigValueDO;
import com.kinghunt.misp.service.api.configx.ConfigService;
import com.bygoodtech.kings.common.dto.ApiResult;
import com.bygoodtech.kings.common.enums.ResultCode;
import com.bygoodtech.kings.common.exception.ServiceResponseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ConfigServiceImpl implements ConfigService {
    @Autowired
    ConfigValueDAO configValueDAO;
    @Autowired
    StringRedisTemplate redisTemplate;

    @Resource
    UploadFileConfig uploadFileConfig;

    @Override
    public ApiResult<SysConfigDTO> getSysConfig() {
        SysConfigDTO sysConfigDTO = new SysConfigDTO();
        getConfigs(0L, sysConfigDTO);
        return ApiResult.ok(sysConfigDTO);
    }

    @Override
    public ApiResult<OrgConfigDTO> getOrgConfig(Long orgId) {
        OrgConfigDTO orgConfigDTO = new OrgConfigDTO();
        orgConfigDTO.setOrgId(orgId);
        getConfigs(orgId, orgConfigDTO);
        return ApiResult.ok(orgConfigDTO);
    }

    @Override
    public ApiResult<Boolean> updateSysConfig(SysConfigDTO sysConfigDTO) {
        updateConfigs(0L, sysConfigDTO);
        return ApiResult.ok(Boolean.TRUE);
    }

    @Override
    public ApiResult<Boolean> updateOrgConfig(Long orgId, OrgConfigDTO orgConfigDTO) {
        updateConfigs(orgId, orgConfigDTO);

        updateOrgRedis(orgId, orgConfigDTO);

        return ApiResult.ok(Boolean.TRUE);
    }

    @Override
    public ApiResult<String> uploadDuplicateCheckFile(UserDTO loginUser, MultipartFile file, Long orgId, HttpServletRequest request) {
        // 机构管理员只能上传自己机构的文件，系统管理员可以上传任意机构文件
        if (RoleEnum.ORG_ADMIN.getId().equals(loginUser.getRoleId()) && loginUser.getOrganizationId() != orgId) {
            throw new ServiceResponseException("您无操作权限，请联系管理员！");
        }

        // 获取绝对路径
        String realPath = uploadFileConfig.getDuplicateCheckPath();
        // 文件放置到机构目录下
        String orgFilePath = orgId + "/";
        // 文件存放的目录
        File folder = new File(realPath + orgFilePath);
        if (!folder.isDirectory()) {
            boolean mkdirSuccess = folder.mkdirs();
            if (!mkdirSuccess) {
                log.error("uploadImg mkdir fail, {}-{}", loginUser.getId(), folder.getAbsolutePath());
                return ApiResult.failOfCode(String.valueOf(ResultCode.ERROR_SERVER_EXCEPTION));
            }
        }
        String oldName = file.getOriginalFilename();

        //文件名
        String fileName = oldName.substring(0, oldName.lastIndexOf("."));
        //文件后缀
        String suffix = Objects.isNull(oldName) ? ".iso" : oldName.substring(oldName.lastIndexOf("."));
        //文件新名字，增加时间戳，防止文件更新后重名
        String newName =  fileName + System.currentTimeMillis() + suffix;
        try {
            File targetFile = new File(folder, newName);

            if (!targetFile.exists()) {
                targetFile.mkdirs();
            } else {
                targetFile.delete();
            }
            file.transferTo(targetFile);
        } catch (IOException e) {
            log.error("uploadImg exception, {}", loginUser.getId(), e);
            return ApiResult.failOfCode(String.valueOf(ResultCode.ERROR_SERVER_EXCEPTION));
        }
        return ApiResult.ok(realPath + orgFilePath + newName);
    }

    /**
     * 获取配置值
     *
     * @param orgId
     * @param object
     */
    private void getConfigs(Long orgId, Object object) {
        List<ConfigValueDO> dos = configValueDAO.selectByOrgId(orgId);
        if (CollectionUtils.isEmpty(dos)) {
            return ;
        }
        Map<String, ConfigValueDO> map = dos.stream().collect(Collectors.toMap(ConfigValueDO::getCode, a -> a));
        Class type;
        for (Field field : object.getClass().getDeclaredFields()) {
            ConfigValueDO configValue = map.get(field.getName());
            if (Objects.isNull(configValue)) {
                continue;
            }
            field.setAccessible(true);
            try {
                type = field.getType();
                Object o = JSON.parseObject(configValue.getValue(), type);
                field.set(object, o);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 更新配置
     *
     * @param orgId
     * @param object
     */
    private void updateConfigs(Long orgId, Object object) {
        List<ConfigValueDO> saveDOs = new ArrayList<>();
        List<ConfigValueDO> updateDOs = new ArrayList<>();
        List<ConfigValueDO> dos = configValueDAO.selectByOrgId(orgId);
        Map<String, ConfigValueDO> map = dos.stream().collect(Collectors.toMap(ConfigValueDO::getCode, a -> a));

        Object value;
        for (Field field : object.getClass().getDeclaredFields()) {
            if ("serialVersionUID".equals(field.getName()) || "orgId".equals(field.getName())) {
                continue;
            }

            field.setAccessible(true);
            try {
                value = field.get(object);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
            ConfigValueDO valueDO = new ConfigValueDO();
            valueDO.setOrgId(orgId);
            valueDO.setCode(field.getName());
            Annotation annotation = field.getAnnotation(Annotation.class);
            if (Objects.nonNull(annotation)) {
                valueDO.setName(annotation.name());
                valueDO.setDescription(annotation.description());
            }
            valueDO.setValue(JSON.toJSONString(value));
            valueDO.setStatus(true);
            // TODO
            valueDO.setType(0);

            ConfigValueDO configValue = map.get(field.getName());
            if (Objects.isNull(configValue)) {
                // TODO
                // valueDO.setCreatorId();
                valueDO.setCreateGmt(new Date());
                saveDOs.add(valueDO);
            } else {
                valueDO.setId(configValue.getId());
                // TODO
                // valueDO.setUpdaterId();
                valueDO.setUpdateGmt(new Date());
                updateDOs.add(valueDO);
            }
        }
        if (!CollectionUtils.isEmpty(saveDOs)) {
            configValueDAO.insertBatch(saveDOs);
        }
        if (!CollectionUtils.isEmpty(updateDOs)) {
            configValueDAO.updateBatch(updateDOs);
        }
    }

    /**
     * 更新Redis
     *
     * @param orgId
     * @param orgConfigDTO
     */
    private void updateOrgRedis(Long orgId, OrgConfigDTO orgConfigDTO) {
        String duplicateCheckKey = RedisKeyPrefix.DUPLICATE_CHECK_ISBN + String.valueOf(orgId);
        redisTemplate.opsForSet().getOperations().delete(duplicateCheckKey);
        List<String> files = orgConfigDTO.getDuplicateCheckFiles();
        if (CollectionUtils.isNotEmpty(files)) {
            List<String> isbns = new ArrayList<>();
            for (String filePath : files) {
                //List<String> isbnsFromFile = CnMarcMoreInterface.getIsbnsFromFile(filePath);
                //isbns.addAll(isbnsFromFile);
            }
            log.info("updateOrgRedis isbn size : {}", isbns.size());
            if (CollectionUtils.isNotEmpty(isbns)) {
                redisTemplate.opsForSet().add(duplicateCheckKey, isbns.toArray(new String[0]));
            }
        }
        // 设置过期时间，默认五天
        redisTemplate.expire(duplicateCheckKey, 5, TimeUnit.DAYS);
    }
}
